/**
 * Alipay.com Inc.
 * Copyright (c) 2004-2016 All Rights Reserved.
 */
package com.alipay.study.core.security;

import java.util.Arrays;

import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

/**
 * 
 * @author wb-qlj205528
 * @version $Id: WebSpringSecurityConfig.java, v 0.1 2016年11月9日 下午4:31:10 wb-qlj205528 Exp $
 */
//@EnableWebSecurity
public class WebSpringSecurityConfigT extends WebSecurityConfigurerAdapter {

    /** 
     * @see org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#configure(org.springframework.security.config.annotation.web.builders.HttpSecurity)
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().regexMatchers("/").permitAll().and().formLogin()
            .loginPage("/login").permitAll().loginProcessingUrl("/login").and().logout()
            .logoutUrl("/logout");
        /*
         * 安全相关设置
         */
        /**
         * csrf
         *  开启csrf校验，disable=true; 
         * 需要在form中加入<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
         * 或者在head中加入<meta name="_csrf" content="${_csrf.token}"/> <meta name="_csrf_header" content="${_csrf.headerName}"/> 
         * 然后在发送ajax请求的时候将  _csrf.headerName:_csrf.token  放入请求header里面
         <form action="./upload?${_csrf.parameterName}=${_csrf.token}" method="post" enctype="multipart/form-data">
           */
        http.csrf().disable();
        /**
         * 
         */
        http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());

        /**
         * 设置ajax跨域调用限制
         * cors  需要配合corsConfigurationSource() 使用
         */
        http.cors();

        /*
         * 关于header配置的作用，参考：http://www.cnblogs.com/doseoer/p/5676297.html
         */
        /**
         * 启用 Cache-Control
         * Cache-Control: no-cache, no-store, max-age=0, must-revalidate
         * Pragma: no-cache
         * Expires: 0
         */
        http.headers().defaultsDisabled().cacheControl();
        /**
         *禁用http response header 
         */
        http.headers().disable();
        /**
         * IE的文档类型自动判断功能
         * 启用 X-Content-Type-Options 
         * X-Content-Type-Options: nosniff   
         */
        http.headers().defaultsDisabled()
        /**
         * nosniff – 这个是唯一正确的设置。  
         */
        .contentTypeOptions();

        /**
         * 强制使用https访问
         * 启动 HSTS 
         * Strict-Transport-Security: max-age=31536000 ; includeSubDomains
         */
        http.headers().httpStrictTransportSecurity()
        /**
         * max-age=31536000 – 告诉浏览器将域名缓存到STS list里面，时间是一年。 
         * max-age=31536000; includeSubDomains – 告诉浏览器将域名缓存到STS list里面并且包含所有的子域名，时间是一年。  
         * max-age=0 – 告诉浏览器移除在STS缓存里的域名，或者不保存此域名。  
         */
        .includeSubDomains(true).maxAgeInSeconds(31536000);

        /**
         * 启用 Public-Key-Pins-Report-Only 
         * Public-Key-Pins-Report-Only: max-age=5184000 ; pin-sha256="d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=" ; pin-sha256="E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=" ; report-uri="http://example.net/pkp-report" ; includeSubDomains
         */
        http.headers()
            /**
             * max-age=3000; pin-sha256=”d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=”; – 规定此站点有3000秒的时间来对x.509证书项目中的公共密钥信息（引号里面的内容）做sha256哈希运算再做base64编码。  
             * max-age=3000; pin-sha256=”d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=”; report-uri=”http://example.com/pkp-report” – 同上面一样，区别是可以报告异常。 
             */
            .httpPublicKeyPinning()
            .includeSubDomains(true)
            .reportUri("http://example.net/pkp-report")
            .addSha256Pins("d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=",
                "E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=");
        /**
         * 启用 X-Frame-Options 
         */
        http.headers()
        /**
         * X-Frame-Options: DENY   禁止一切iframe调用 
         */
        .frameOptions()
        /**
         * X-Frame-Options: SAMEORIGIN   允许同域下的iframe调用
         */
        .sameOrigin();

        /**
         * 防止浏览器中的反射性xss
         * 启用 X-XSS-Protection 
         * X-XSS-Protection: 1; mode=block
         */
        http.headers()
        /**
         * 0 – 关闭对浏览器的xss防护 
         * 1 – 开启xss防护
         * 1; mode=block – 开启xss防护并通知浏览器阻止而不是过滤用户注入的脚本。 
         * 1; report=http://site.com/report – 这个只有chrome和webkit内核的浏览器支持，这种模式告诉浏览器当发现疑似xss攻击的时候就将这部分数据post到指定地址。  
         */
        .xssProtection().block(true);

        /**
         * 用来定义页面可以加载哪些资源，减少XSS的发生
         * 启用 Content-Security-Policy   多个指令之间用英文分号分割
         * Content-Security-Policy: default-src 'self'; script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/
         */
        http.headers()
            /**
             * 'self'表示本域名下的资源，设定其他域需要指定域名如https://trustedscripts.example.com 多个设置之间用空格分隔
             * default-src 定义针对所有类型（js、image、css、web font，ajax 请求，iframe，多媒体等）资源的默认加载策略，某类型资源如果没有单独定义策略，就使用默认的
             * script-src  定义针对 JavaScript 的加载策略。
             * style-src   定义针对样式的加载策略。
             * img-src     定义针对图片的加载策略
             * connect-src 针对 Ajax、WebSocket 等请求的加载策略。不允许的情况下，浏览器会模拟一个状态为 400 的响应
             * font-src    针对 WebFont 的加载策略。
             * object-src  针对 <object>、<embed> 或 <applet> 等标签引入的 flash 等插件的加载策略。
             * media-src   针对 <audio> 或 <video> 等标签引入的 HTML 多媒体的加载策略。
             * frame-src   针对 frame 的加载策略。
             * sandbox     对请求的资源启用 sandbox（类似于 iframe 的 sandbox 属性）。
             * report-uri  告诉浏览器如果请求的资源不被策略允许时，往哪个地址提交日志信息。 特别的：如果想让浏览器只汇报日志，不阻止任何内容，可以改用 Content-Security-Policy-Report-Only 头
             */
            .contentSecurityPolicy(
                "default-src 'self'; script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/")
            /**
            * 
            * Content-Security-Policy-Report-Only: script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/
            */
            .reportOnly();

    }

    /**
     * cors配置
     * 
     * @return
     */
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        /**
         * 设置允许来自哪些域名的ajax跨域调用
         */
        configuration.setAllowedOrigins(Arrays.asList("https://example.com"));
        /**
         * 设置允许哪几种请求方式可以跨域访问
         */
        configuration.setAllowedMethods(Arrays.asList("GET", "POST"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        /**
         * 指定configuration设置的作用范围
         */
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}
